﻿using System;
using System.IO;
using System.Windows.Forms;
using System.Drawing;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Linq.Expressions;


namespace MinecraftBackupAssistant
{
    public static class Utility
    {
        public static readonly string ZipComment = "Created by Minecraft Backup Assistant, written by omer681";
        public static readonly string TemporaryPath = Utility.CombinePath(Path.GetTempPath(), "MinecraftBackupAssistant");
        public static readonly string TemporaryBackupPath = Utility.CombinePath(Path.GetTempPath(), "MinecraftBackupAssistant", "Backup");
        public static readonly string TemporaryMapperPath = Utility.CombinePath(Path.GetTempPath(), "MinecraftBackupAssistant", "Mapper");
        public static readonly string TemporarySwapPath = Utility.CombinePath(Path.GetTempPath(), "MinecraftBackupAssistant", "Swap");
        public static readonly System.Text.RegularExpressions.Regex NameRegex = new System.Text.RegularExpressions.Regex(@"(?<World>.*?)_(?<Date>.*?)(?:\.(?<Description>.*?)(?:\.?zip|$)|$)", System.Text.RegularExpressions.RegexOptions.IgnoreCase);
        public static readonly string MapperExtension = ".png";
        public static readonly string CurrentVersion = "2.5.1";

        public static void HandleError(Exception e)
        {
            new Forms.ErrorReportForm(e).Show();
        }

        public static bool IsNumeric(this string str)
        {
            double d;
            return Double.TryParse(str, out d);
        }
    

        public static void DeleteDirectory(string directory)
        {
            //I had some problems with the code running too fast and not deleting the whole directory.
            //This way it should take about 1-2 attempts.
            while (Directory.Exists(directory))
            {
                try
                {
                    new DirectoryInfo(directory).Delete(true);
                }
                catch { }
            }
        }

		public static void CopyStream(Stream input, Stream output)
		{
			byte[] buffer = new byte[8 * 1024];
			int len;
			while ((len = input.Read(buffer, 0, buffer.Length)) > 0)
			{
				output.Write(buffer, 0, len);
			}
		}

        public static int GetEntriesInDirectory(string dir)
        {
            return GetEntriesInDirectory(new DirectoryInfo(dir));
        }
        public static int GetEntriesInDirectory(DirectoryInfo dir)
        {
            if (!dir.Exists)
                return 0;
            return dir.GetFiles("*.*", SearchOption.AllDirectories).Length + dir.GetFiles("*.*", SearchOption.AllDirectories).Length;
        }

        public static string CombinePath(string arg1, params string[] args)
        {
            string path = arg1;
            foreach (string arg in args)
                path = Path.Combine(path, arg);
            
            return path;
        }

        public static bool IsDirectoryEmpty(string dir)
        {
            if (!Directory.Exists(dir))
                return true;

            return Directory.GetDirectories(dir, "*.*", SearchOption.AllDirectories).Length == 0 && Directory.GetFiles(dir, "*.*", SearchOption.AllDirectories).Length == 0;
        }

        public static void PrepareDirectories()
        {
			Utility.DeleteDirectory(TemporaryPath);

            Directory.CreateDirectory(TemporaryPath);
            Directory.CreateDirectory(TemporaryBackupPath);
            Directory.CreateDirectory(TemporarySwapPath);
            Directory.CreateDirectory(TemporaryMapperPath);
        }


        public static bool Contains(this List<KeyValuePair<string, DateTime>> list, string value)
        {
            return (
                from keyValue in list
                select keyValue.Key == value
                ).Contains(true);
        }

        public static T GetPropertyDefaultValue<T>(string propertyName, bool isSerialized)
        {
            object defaultValue = Properties.Settings.Default.Properties[propertyName].DefaultValue;
            if (isSerialized)
            {
                System.Xml.Serialization.XmlSerializer xs = new System.Xml.Serialization.XmlSerializer(typeof(T));
                MemoryStream ms = new MemoryStream(Encoding.Unicode.GetBytes((string)defaultValue));
                ms.Position = 0;

                return (T)xs.Deserialize(ms);
            }
            return (T)defaultValue;
        }

        public static T Clone<T>(this T source)
        {
            if (!typeof(T).IsSerializable)
            {
                throw new ArgumentException("The type must be serializable.", "source");
            }

            // Don't serialize a null object, simply return the default for that object
            if (Object.ReferenceEquals(source, null))
            {
                return default(T);
            }

            System.Runtime.Serialization.IFormatter formatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
            Stream stream = new MemoryStream();
            using (stream)
            {
                formatter.Serialize(stream, source);
                stream.Seek(0, SeekOrigin.Begin);
                return (T)formatter.Deserialize(stream);
            }
        }

        public static void AdjustComboBoxWidth(ComboBox cb)
        {
            int widestWidth = cb.DropDownWidth; // adjust the width of the combo listbox
            string valueToMeasure;
            int currentWidth;

            using (Graphics g = cb.CreateGraphics())
            {
                for (int i = 0; i < cb.Items.Count; i++)
                {
                    valueToMeasure = cb.GetItemText(cb.Items[i]);

                    currentWidth = (int)g.MeasureString(valueToMeasure, cb.Font).Width;
                    if (currentWidth > widestWidth) { widestWidth = currentWidth; }
                }
            }

            cb.DropDownWidth = widestWidth;
        }


        public static Bitmap CropWhitespace(Bitmap bm)
        {
            int width = bm.Width;
            int height = bm.Height;
            int topRow = 0;
            int bottomRow = 0;
            int leftColumn = 0;
            int rightColumn = 0;

            for (int row = 0; row < height; ++row)
            {
                if (allWhiteRow(row, bm))
                    topRow = row;
                else break;
            }
            for (int row = height - 1; row >= 0; --row)
            {
                bottomRow = row;
                if (allWhiteRow(row, bm))
                    bottomRow = row;
                else break;
            }

            for (int col = 0; col < width; ++col)
            {
                if (allWhiteColumn(col, bm))
                    leftColumn = col;
                else
                    break;
            }
            for (int col = width - 1; col >= 0; --col)
            {
                rightColumn = col;
                if (allWhiteColumn(col, bm))
                    rightColumn = col;
                else
                    break;
            }

            int croppedWidth = rightColumn - leftColumn;
            int croppedHeight = bottomRow - topRow;

            Bitmap target = new Bitmap(croppedWidth, croppedHeight);
            using (Graphics g = Graphics.FromImage(target))
            {
                g.DrawImage(bm,
                  new RectangleF(0, 0, croppedWidth, croppedHeight),
                  new RectangleF(leftColumn, topRow, croppedWidth, croppedHeight),
                  GraphicsUnit.Pixel);
            }
            return target;

        }
        private static bool allWhiteRow(int row, Bitmap bm)
        {
            for (int i = 0; i < bm.Width; ++i)
            {
                Color pixel = bm.GetPixel(i, row);
                if (pixel.A != 0)
                    return false;
            }
            return true;
        }
        private static bool allWhiteColumn(int col, Bitmap bm)
        {
            for (int i = 0; i < bm.Height; ++i)
            {
                Color pixel = bm.GetPixel(col, i);
                if (pixel.A != 0)
                    return false;
            }
            return true;
        }
    }

    struct BackupEntry : IComparable<BackupEntry>
    {
        public string Name;
        public DateTime DateTime;
        public bool IsZip;

        public BackupEntry(string name, DateTime dateTime, bool isZip)
        {
            Name = name;
            DateTime = dateTime;
            IsZip = isZip;
        }

        public int CompareTo(BackupEntry other)
        {
            return this.DateTime.CompareTo(other.DateTime);
        }
    }
}
